{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1331faa1",
   "metadata": {},
   "source": [
    "可以在[Bookshop.org](https://bookshop.org/a/98697/9781098155438) 和\n",
    "[Amazon](https://www.amazon.com/_/dp/1098155432?smid=ATVPDKIKX0DER&_encoding=UTF8&tag=oreilly20-20&_encoding=UTF8&tag=greenteapre01-20&linkCode=ur2&linkId=e2a529f94920295d27ec8a06e757dc7c&camp=1789&creative=9325)获取纸制版和电子版的*Think Python 3e*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "df64b7da",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from os.path import basename, exists\n",
    "\n",
    "def download(url):\n",
    "    filename = basename(url)\n",
    "    if not exists(filename):\n",
    "        from urllib.request import urlretrieve\n",
    "\n",
    "        local, _ = urlretrieve(url, filename)\n",
    "        print(\"Downloaded \" + str(local))\n",
    "    return filename\n",
    "\n",
    "download('https://gitee.com/regentsai/Think_Python_3e_CN/blob/master/thinkpython.py');\n",
    "download('https://gitee.com/regentsai/Think_Python_3e_CN/blob/master/diagram.py');\n",
    "download('https://gitee.com/regentsai/Think_Python_3e_CN/blob/master/jupyturtle.py');"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "320fc8bc",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import thinkpython\n",
    "\n",
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fbb4d5a2",
   "metadata": {},
   "source": [
    "# 第4章：函数和接口\n",
    "\n",
    "本章介绍名为`jupyturtle`的模块，这个模块允许你指挥一只想象中的海龟，创建简单的绘画。我们会使用这个模块绘制方块，多边形和圆，并展示**界面设计interface design**。界面设计是设计一起协作的函数的一种方式。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b0efa00",
   "metadata": {
    "tags": []
   },
   "source": [
    "##  jupyturtle模块\n",
    "\n",
    "要使用`jupyturtle`模块，我们可以像这样进行导入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8f5a8a45",
   "metadata": {},
   "outputs": [],
   "source": [
    "import jupyturtle"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c801121",
   "metadata": {},
   "source": [
    "现在我们可以使用模块中定义的函数，例如`make_turtle`和`forward`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b3f255cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"250.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,250.0,75.0) translate(250.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "jupyturtle.make_turtle()\n",
    "jupyturtle.forward(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77a61cbb",
   "metadata": {},
   "source": [
    "`make_turtle`创建一个**画布canvas**，在画布的空间内我们可以绘画；`make_turtle`还会创建一只海龟，由圆形的壳和三角形的头表示。圆圈显示了海龟的位置，而三角形表示了海龟的面向方向。\n",
    "\n",
    "我们将多次使用`jupyturtle`中定义的函数，因此不每次带上模块的名字会比较方便。如果我们像这样导入模块，就能做到："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "234fde81",
   "metadata": {},
   "outputs": [],
   "source": [
    "from jupyturtle import make_turtle, forward"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1322d31",
   "metadata": {},
   "source": [
    "这种导入语句从`jupyturtle`模块导入`make_turtle`和`forward`函数，我们可以直接调用他们："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1e768880",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"250.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,250.0,75.0) translate(250.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "forward(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd319754",
   "metadata": {},
   "source": [
    "`jupyturtle`还提供了两个函数，`left`与`right`。我们将像这样进行导入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6d874b03",
   "metadata": {},
   "outputs": [],
   "source": [
    "from jupyturtle import left, right"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0da2a311",
   "metadata": {},
   "source": [
    "`left`函数将让海龟向左转，它接受一个参数，即向左转的度数。例如，我们可以让海龟向左转90度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "1bb57a0c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(180.0,200.0,25.0) translate(200.0, 25.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "forward(50)\n",
    "left(90)\n",
    "forward(50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cea2940f",
   "metadata": {},
   "source": [
    "这段程序让海龟向东移动，然后向北移动，在海龟的身后留下两个线段。在你继续学习之前，看看你能否调整这段程序，绘制一个方块。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e20ea96c",
   "metadata": {},
   "source": [
    "## 绘制一个方块\n",
    "\n",
    "以下是绘制方块的一种方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9a9e455f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "\n",
    "forward(50)\n",
    "left(90)\n",
    "\n",
    "forward(50)\n",
    "left(90)\n",
    "\n",
    "forward(50)\n",
    "left(90)\n",
    "\n",
    "forward(50)\n",
    "left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7500957",
   "metadata": {},
   "source": [
    "由于相同的代码片段重复了4次，我们可以用`for`循环写地更简洁："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cc27ad66",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "for i in range(4):\n",
    "    forward(50)\n",
    "    left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c072ea41",
   "metadata": {
    "tags": []
   },
   "source": [
    "## 封装和泛化\n",
    "\n",
    "现在让我们将绘制方块的代码放到名为`square`的函数中。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ad5f1128",
   "metadata": {},
   "outputs": [],
   "source": [
    "def square():\n",
    "    for i in range(4):\n",
    "        forward(50)\n",
    "        left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0789b5d9",
   "metadata": {},
   "source": [
    "然后我们可以像这样调用函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "193bbe5e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "square()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da905fc6",
   "metadata": {},
   "source": [
    "将一段代码包围在函数中的动作称作**封装encapsulation**。封装的好处之一是它将这段代码与一个名字联系在一起，成为某种意义上的文档；另一个好处在于，如果你重新使用这段代码，调用两次函数要比复制粘贴函数体更简洁！\n",
    "\n",
    "在目前的版本中，方块的大小始终为`50`。如果我们想要绘制不同大小的方块，我们可以将边长`length`作为参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "def8a5f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def square(length):\n",
    "    for i in range(4):\n",
    "        forward(length)\n",
    "        left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "397fda4b",
   "metadata": {},
   "source": [
    "现在我们可以绘制不同大小的方块了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "b283e795",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"180.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.0\" y1=\"75.0\" x2=\"180.0\" y2=\"45.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.0\" y1=\"45.0\" x2=\"150.0\" y2=\"45.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"45.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"75.0\" x2=\"210.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"210.0\" y1=\"75.0\" x2=\"210.0\" y2=\"15.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"210.0\" y1=\"15.0\" x2=\"150.0\" y2=\"15.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"15.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "square(30)\n",
    "square(60)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a46bf64",
   "metadata": {},
   "source": [
    "给一个函数添加参数称为**泛化generalization**，因为它让函数变得更加泛用：在之前的版本，方块的大小始终不变，而现在的版本方块大小可以进行修改。\n",
    "\n",
    "如果我们添加额外的参数，我们可以让函数更加泛用。下面的函数绘制给定数量边数的正多边形："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "171974ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "def polygon(n, length):\n",
    "    angle = 360 / n\n",
    "    for i in range(n):\n",
    "        forward(length)\n",
    "        left(angle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "286d3c77",
   "metadata": {},
   "source": [
    "正`n`边形相邻边的夹角为`360 / n`度。\n",
    "\n",
    "下面的例子绘制了一个边长为`30`的正七边形："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "71f7d9d2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"180.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.0\" y1=\"75.0\" x2=\"198.7\" y2=\"51.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"198.7\" y1=\"51.5\" x2=\"192.0\" y2=\"22.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"192.0\" y1=\"22.3\" x2=\"165.0\" y2=\"9.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"165.0\" y1=\"9.3\" x2=\"138.0\" y2=\"22.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"138.0\" y1=\"22.3\" x2=\"131.3\" y2=\"51.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"131.3\" y1=\"51.5\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(270.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "polygon(7, 30)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc0226db",
   "metadata": {},
   "source": [
    "当函数有许多实参时，很容易忘记它们是什么，或者它们的顺序是什么。在实参列表中包含形参的名字是个好主意。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "8ff2a5f4",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"180.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.0\" y1=\"75.0\" x2=\"198.7\" y2=\"51.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"198.7\" y1=\"51.5\" x2=\"192.0\" y2=\"22.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"192.0\" y1=\"22.3\" x2=\"165.0\" y2=\"9.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"165.0\" y1=\"9.3\" x2=\"138.0\" y2=\"22.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"138.0\" y1=\"22.3\" x2=\"131.3\" y2=\"51.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"131.3\" y1=\"51.5\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(270.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "polygon(n=7, length=30)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6aa28eba",
   "metadata": {},
   "source": [
    "这些实参有时称作“具名实参”(named arguments)，因为它们包含形参名。但在Python中它们更常被称作**关键字参数keyword arguments**（不要与Python的关键字相混淆，如`for`和`def`）。\n",
    "\n",
    "这里使用了赋值运算符`=`，提醒我们实参和形参是如何工作的：当你调用函数，实参被赋值给形参。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b10184b4",
   "metadata": {},
   "source": [
    "## 近似圆\n",
    "\n",
    "现在假设我们要画一个圆。我们可以画一个有很多边的多边形，从而让每条边足够小，难以看到。下面是使用`polygon`绘制正30边形的近似圆。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7f2a5f28",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "def circle(radius):\n",
    "    circumference = 2 * math.pi * radius\n",
    "    n = 30\n",
    "    length = circumference / n\n",
    "    polygon(n, length)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39023314",
   "metadata": {},
   "source": [
    "`circle`函数接受参数`radius`，作为圆的半径。它会计算`circumference`，即给定半径的圆的周长，`n`是边的数量，所以`circumference / n`是每条边的边长。\n",
    "\n",
    "这个函数可能需要跑一段时间。我们可以在调用`make_turtle`时使用关键字参数`delay`设置每次行动之间海龟的延迟时间，单位为秒。默认值为`0.2`秒，将其设置为`0.02`可以以10倍速绘制。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "75258056",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"156.3\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"156.3\" y1=\"75.0\" x2=\"162.4\" y2=\"73.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"162.4\" y1=\"73.7\" x2=\"168.2\" y2=\"71.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"168.2\" y1=\"71.1\" x2=\"173.3\" y2=\"67.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"173.3\" y1=\"67.4\" x2=\"177.5\" y2=\"62.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"177.5\" y1=\"62.8\" x2=\"180.6\" y2=\"57.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.6\" y1=\"57.3\" x2=\"182.5\" y2=\"51.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"182.5\" y1=\"51.4\" x2=\"183.2\" y2=\"45.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"183.2\" y1=\"45.1\" x2=\"182.5\" y2=\"38.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"182.5\" y1=\"38.9\" x2=\"180.6\" y2=\"32.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.6\" y1=\"32.9\" x2=\"177.5\" y2=\"27.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"177.5\" y1=\"27.4\" x2=\"173.3\" y2=\"22.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"173.3\" y1=\"22.8\" x2=\"168.2\" y2=\"19.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"168.2\" y1=\"19.1\" x2=\"162.4\" y2=\"16.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"162.4\" y1=\"16.5\" x2=\"156.3\" y2=\"15.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"156.3\" y1=\"15.2\" x2=\"150.0\" y2=\"15.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"15.2\" x2=\"143.9\" y2=\"16.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"143.9\" y1=\"16.5\" x2=\"138.1\" y2=\"19.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"138.1\" y1=\"19.1\" x2=\"133.0\" y2=\"22.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"133.0\" y1=\"22.8\" x2=\"128.8\" y2=\"27.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"128.8\" y1=\"27.4\" x2=\"125.7\" y2=\"32.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"125.7\" y1=\"32.9\" x2=\"123.7\" y2=\"38.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"123.7\" y1=\"38.9\" x2=\"123.1\" y2=\"45.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"123.1\" y1=\"45.1\" x2=\"123.7\" y2=\"51.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"123.7\" y1=\"51.4\" x2=\"125.7\" y2=\"57.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"125.7\" y1=\"57.3\" x2=\"128.8\" y2=\"62.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"128.8\" y1=\"62.8\" x2=\"133.0\" y2=\"67.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"133.0\" y1=\"67.4\" x2=\"138.1\" y2=\"71.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"138.1\" y1=\"71.1\" x2=\"143.9\" y2=\"73.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"143.9\" y1=\"73.7\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle(delay=0.02)\n",
    "circle(30)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "701f9cf8",
   "metadata": {},
   "source": [
    "这种方案的限制是`n`是常数，对于很大的圆，边长可能太长，对于很小的圆，绘制30条很短的边很浪费时间。一个方案是将`n`作为参数泛化函数，但让我们先保持简单吧。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c48f262c",
   "metadata": {},
   "source": [
    "## 重构\n",
    "\n",
    "现在让我们编写比`circle`更泛用的版本，名为`arc`函数，接受第2个参数`angle`，绘制给定角度的圆弧。例如，如果`angle`是360度，`arc`将绘制完整的圆，如果`angle`是180度，`arc`将绘制半圆。\n",
    "\n",
    "要编写`circle`函数，我们可以重用`polygon`，因为正多边形是圆的良好近似。但我们不能用`polygon`来编写`arc`。\n",
    "\n",
    "相反，我们将创建`polygon`更泛用的版本，名为`polyline`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "381edd23",
   "metadata": {},
   "outputs": [],
   "source": [
    "def polyline(n, length, angle):\n",
    "    for i in range(n):\n",
    "        forward(length)\n",
    "        left(angle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2b2503e",
   "metadata": {},
   "source": [
    "`polyline`接受的参数为要绘制的线段数量`n`，线段的长度`length`以及相邻线段之间的夹角`angle`。\n",
    "\n",
    "现在我们可以使用`polyline`重新编写`polygon`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "2f4eecc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def polygon(n, length):\n",
    "    angle = 360.0 / n\n",
    "    polyline(n, length, angle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2714a59e",
   "metadata": {},
   "source": [
    "我们也可以使用`polyline`来编写`arc`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "539466f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "def arc(radius, angle):\n",
    "    arc_length = 2 * math.pi * radius * angle / 360\n",
    "    n = 30\n",
    "    length = arc_length / n\n",
    "    step_angle = angle / n\n",
    "    polyline(n, length, step_angle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c18773c",
   "metadata": {},
   "source": [
    "`arc`与`circle`相似，除了它会计算`arc_length`，圆弧长度与圆周长的比和它们的角度之比相同。\n",
    "\n",
    "最后，我们可以用`arc`重写`circle`函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "8e09f456",
   "metadata": {},
   "outputs": [],
   "source": [
    "def circle(radius):\n",
    "    arc(radius,  360)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "313a357c",
   "metadata": {},
   "source": [
    "要检查这些函数是否符合预期，我们将使用它们画一只蜗牛。设置`delay=0`将让海龟跑得尽可能快。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "80d6eadd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"159.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"159.0\" y1=\"75.0\" x2=\"167.6\" y2=\"72.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"167.6\" y1=\"72.2\" x2=\"174.8\" y2=\"66.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"174.8\" y1=\"66.9\" x2=\"180.1\" y2=\"59.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.1\" y1=\"59.6\" x2=\"182.9\" y2=\"51.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"182.9\" y1=\"51.1\" x2=\"182.9\" y2=\"42.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"182.9\" y1=\"42.1\" x2=\"180.1\" y2=\"33.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.1\" y1=\"33.5\" x2=\"174.8\" y2=\"26.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"174.8\" y1=\"26.2\" x2=\"167.6\" y2=\"21.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"167.6\" y1=\"21.0\" x2=\"159.0\" y2=\"18.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"159.0\" y1=\"18.2\" x2=\"150.0\" y2=\"18.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"18.2\" x2=\"141.4\" y2=\"21.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"141.4\" y1=\"21.0\" x2=\"134.2\" y2=\"26.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"134.2\" y1=\"26.2\" x2=\"128.9\" y2=\"33.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"128.9\" y1=\"33.5\" x2=\"126.1\" y2=\"42.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"126.1\" y1=\"42.1\" x2=\"126.1\" y2=\"51.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"126.1\" y1=\"51.1\" x2=\"128.9\" y2=\"59.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"128.9\" y1=\"59.6\" x2=\"134.2\" y2=\"66.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"134.2\" y1=\"66.9\" x2=\"141.4\" y2=\"72.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"141.4\" y1=\"72.2\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"75.0\" x2=\"152.9\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"152.9\" y1=\"75.0\" x2=\"155.7\" y2=\"74.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"155.7\" y1=\"74.9\" x2=\"158.5\" y2=\"74.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"158.5\" y1=\"74.7\" x2=\"161.4\" y2=\"74.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"161.4\" y1=\"74.3\" x2=\"164.2\" y2=\"73.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"164.2\" y1=\"73.8\" x2=\"167.0\" y2=\"73.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"167.0\" y1=\"73.3\" x2=\"169.7\" y2=\"72.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"169.7\" y1=\"72.6\" x2=\"172.5\" y2=\"71.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"172.5\" y1=\"71.8\" x2=\"175.2\" y2=\"70.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"175.2\" y1=\"70.9\" x2=\"177.8\" y2=\"69.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"177.8\" y1=\"69.8\" x2=\"180.5\" y2=\"68.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"180.5\" y1=\"68.7\" x2=\"183.0\" y2=\"67.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"183.0\" y1=\"67.5\" x2=\"185.5\" y2=\"66.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"185.5\" y1=\"66.1\" x2=\"188.0\" y2=\"64.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"188.0\" y1=\"64.7\" x2=\"190.4\" y2=\"63.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"190.4\" y1=\"63.2\" x2=\"192.7\" y2=\"61.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"192.7\" y1=\"61.5\" x2=\"195.0\" y2=\"59.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"195.0\" y1=\"59.8\" x2=\"197.2\" y2=\"58.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"197.2\" y1=\"58.0\" x2=\"199.3\" y2=\"56.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"199.3\" y1=\"56.1\" x2=\"201.4\" y2=\"54.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"201.4\" y1=\"54.1\" x2=\"203.3\" y2=\"52.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"203.3\" y1=\"52.0\" x2=\"205.2\" y2=\"49.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"205.2\" y1=\"49.9\" x2=\"207.0\" y2=\"47.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"207.0\" y1=\"47.6\" x2=\"208.7\" y2=\"45.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"208.7\" y1=\"45.3\" x2=\"210.2\" y2=\"43.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"210.2\" y1=\"43.0\" x2=\"211.7\" y2=\"40.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"211.7\" y1=\"40.5\" x2=\"213.1\" y2=\"38.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"213.1\" y1=\"38.1\" x2=\"214.4\" y2=\"35.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"214.4\" y1=\"35.5\" x2=\"215.6\" y2=\"32.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"215.6\" y1=\"32.9\" x2=\"216.7\" y2=\"30.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"216.7\" y1=\"30.3\" x2=\"217.4\" y2=\"28.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"217.4\" y1=\"28.3\" x2=\"217.7\" y2=\"26.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"217.7\" y1=\"26.2\" x2=\"217.6\" y2=\"24.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"217.6\" y1=\"24.2\" x2=\"217.0\" y2=\"22.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"217.0\" y1=\"22.1\" x2=\"216.0\" y2=\"20.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"216.0\" y1=\"20.3\" x2=\"214.7\" y2=\"18.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"214.7\" y1=\"18.7\" x2=\"213.0\" y2=\"17.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"213.0\" y1=\"17.4\" x2=\"211.1\" y2=\"16.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"211.1\" y1=\"16.5\" x2=\"209.1\" y2=\"16.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"209.1\" y1=\"16.0\" x2=\"207.0\" y2=\"15.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"207.0\" y1=\"15.9\" x2=\"204.9\" y2=\"16.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"204.9\" y1=\"16.3\" x2=\"203.0\" y2=\"17.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"203.0\" y1=\"17.0\" x2=\"201.3\" y2=\"18.2\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"201.3\" y1=\"18.2\" x2=\"199.8\" y2=\"19.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"199.8\" y1=\"19.7\" x2=\"198.7\" y2=\"21.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"198.7\" y1=\"21.5\" x2=\"198.0\" y2=\"23.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"198.0\" y1=\"23.5\" x2=\"197.7\" y2=\"25.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"197.7\" y1=\"25.5\" x2=\"197.8\" y2=\"27.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"197.8\" y1=\"27.6\" x2=\"198.4\" y2=\"29.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"198.4\" y1=\"29.6\" x2=\"199.4\" y2=\"31.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"199.4\" y1=\"31.5\" x2=\"200.7\" y2=\"33.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.7\" y1=\"33.1\" x2=\"202.4\" y2=\"34.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"202.4\" y1=\"34.4\" x2=\"204.3\" y2=\"35.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"204.3\" y1=\"35.3\" x2=\"206.3\" y2=\"35.8\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"206.3\" y1=\"35.8\" x2=\"208.4\" y2=\"35.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"208.4\" y1=\"35.9\" x2=\"210.5\" y2=\"35.5\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"210.5\" y1=\"35.5\" x2=\"212.4\" y2=\"34.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"212.4\" y1=\"34.7\" x2=\"214.1\" y2=\"33.6\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"214.1\" y1=\"33.6\" x2=\"215.6\" y2=\"32.1\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"215.6\" y1=\"32.1\" x2=\"216.7\" y2=\"30.3\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(200.0,216.7,30.3) translate(216.7, 30.3)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle(delay=0)\n",
    "polygon(n=20, length=9)\n",
    "arc(radius=70, angle=70)\n",
    "circle(radius=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a34da3d8",
   "metadata": {},
   "source": [
    "在这个例子里面，我们从可以运行的代码开始，用不同的函数重新组织代码。像这样改善代码，但不改变代码的行为的过程称作**重构refactoring**。\n",
    "\n",
    "如果我们先进行计划，我们也许可以直接先写`polyline`，避免重构，但是通常你在项目开始时不可能知道太多。当你开始编程，你对问题的理解会更加深入。有时重构标志着你学到了一些东西。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d18c9d16",
   "metadata": {},
   "source": [
    "## 栈图\n",
    "\n",
    "当调用`circle`时，将调用`arc`，而`arc`将调用`polyline`。我们可以用栈图显示函数调用的序列与每个函数的形参接收的值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "1571ee71",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from diagram import make_binding, make_frame, Frame, Stack\n",
    "\n",
    "frame1 = make_frame(dict(radius=30), name='circle', loc='left')\n",
    "\n",
    "frame2 = make_frame(dict(radius=30, angle=360), name='arc', loc='left', dx=1.1)\n",
    "\n",
    "frame3 = make_frame(dict(n=60, length=3.04, angle=5.8), \n",
    "                    name='polyline', loc='left', dx=1.1, offsetx=-0.27)\n",
    "\n",
    "stack = Stack([frame1, frame2, frame3], dy=-0.4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "f4e37360",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 358x131 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from diagram import diagram, adjust\n",
    "\n",
    "width, height, x, y = [3.58, 1.31, 0.98, 1.06]\n",
    "ax = diagram(width, height)\n",
    "bbox = stack.draw(ax, x, y)\n",
    "#adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3160bba1",
   "metadata": {},
   "source": [
    "注意`polyline`中的`angle`与`arc`中的`angle`值并不相同。形参是局部的，意味着你可以在不同的函数中使用相同的形参；它在每个函数里面是不同的变量，并且可以指向不同的值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c23552d3",
   "metadata": {},
   "source": [
    "## 开发计划\n",
    "\n",
    "**开发计划development plan**是编写程序的过程。本章使用的过程是“封装和泛化”。该过程如下：\n",
    "\n",
    "1. 从没有函数定义的小程序开始编写；\n",
    "2. 一旦该程序正确运行，确定一个连续的部分，将其封装到函数中并命名；\n",
    "3. 添加合适的参数，让函数更加泛用；\n",
    "4. 重复步骤1～3直到你获得了一组正确的函数；\n",
    "5. 寻求机会，通过重构改善代码。例如，如果你在几个地方有类似的代码，考虑将其重构到合适的泛用函数中。\n",
    "\n",
    "这个过程有一些缺陷，我们稍后会看到替代过程，但在你不知道如何将程序分解为函数时这个过程很有用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3b6b83d",
   "metadata": {},
   "source": [
    "函数的设计分为两部分：\n",
    "\n",
    "- **界面interface**规定了函数如何使用，包括函数名，接受的参数和函数的用途；\n",
    "- **实现implementation**是函数具体完成工作的方式。\n",
    "\n",
    "例如，下面第1个版本的`circle`函数使用了`polygon`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "baf964ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "def circle(radius):\n",
    "    circumference = 2 * math.pi * radius\n",
    "    n = 30\n",
    "    length = circumference / n\n",
    "    polygon(n, length)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d3d2e79",
   "metadata": {},
   "source": [
    "而使用`arc`的重构版本如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "e2e006d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def circle(radius):\n",
    "    arc(radius,  360)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b726f72c",
   "metadata": {},
   "source": [
    "这两个函数的界面相同：接受相同的参数，完成相同的功能，但它们的实现是不同的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e3bae20",
   "metadata": {
    "tags": []
   },
   "source": [
    "## 文档字符串\n",
    "\n",
    "**文档字符串docstring**是在函数体第一行的字符串，解释了函数的界面。“doc”是文档“documentation”的缩写。\n",
    "\n",
    "下面是一个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "b68f3682",
   "metadata": {},
   "outputs": [],
   "source": [
    "def polyline(n, length, angle):\n",
    "    \"\"\"绘制一条折线，规定了线段的数量，长度和相邻线段之间的夹角。\n",
    "    \n",
    "    n: 整数，线段的数量\n",
    "    length: 线段长度\n",
    "    angle: 相邻线段之间的夹角，单位为角度\n",
    "    \"\"\"    \n",
    "    for i in range(n):\n",
    "        forward(length)\n",
    "        left(angle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55b60cbc",
   "metadata": {},
   "source": [
    "习惯上，文档字符串是三引号字符串(triple-quoted strings)，也被称为**多行字符串multiline strings**，因为三引号字符串允许字符串跨越多行。\n",
    "\n",
    "译注：三引号可以用成对的3个连续单引号包围，也可以用成对的3个连续双引号包围，但是不能混用。\n",
    "\n",
    "文档字符串应该：\n",
    "\n",
    "- 简洁解释函数的作用，不详细解释它是如何实现的；\n",
    "- 如果有的参数的类型不明确，标明参数的类型\n",
    "\n",
    "编写这种文档是接口设计的重要部分。良好定义的接口应该易于解释；如果你难以解释某个函数，这个函数的接口可能有待改善。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1115940",
   "metadata": {},
   "source": [
    "## 调试\n",
    "\n",
    "接口像是一份函数与调用者之间的合同。调用者同意提供特定的参数，函数同意完成特定的工作。\n",
    "\n",
    "例如，`polyline`要求3个参数：`n`必须是整数，`length`应该是正数，`angle`应该是代表角度的实数。\n",
    "\n",
    "这些要求称作**前置条件preconditions**，因为它们应该在函数执行之前成立。相反，函数结束时的条件是**后置条件postconditions**。后置条件包括函数预期的作用（如绘制线段）以及副作用（如移动海龟，或者做了其他改变）。\n",
    "\n",
    "满足前置条件是调用者的责任。如果调用者违反前置条件，函数没有正确工作，那么问题在于调用者，而不在函数中。\n",
    "\n",
    "如果满足了前置条件，而后置条件没有满足，则问题在函数中。如果你的前置条件和后置条件清晰，则可以帮助你进行调试。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4d33a70",
   "metadata": {},
   "source": [
    "## 术语表\n",
    "\n",
    "- **界面设计interface design**：设计函数界面的过程，包括函数应该接受的参数；\n",
    "- **画布canvas**：一个窗口，展示线段，圆，矩形和其他形状组成的图形元素；\n",
    "- **封装encapsulation**：将语句序列转移到函数定义中的过程；\n",
    "- **泛化generalization**：将一些不必要的指定值（如数字）替换为合适的泛用对象（如变量和形参）的过程；\n",
    "- **关键字参数keyword argument**：指名形参名的实参\n",
    "- **重构refactoring**：调整能够运行的程序，改善函数的接口和其他质量；\n",
    "- **开发计划development plan**：编写代码的过程；\n",
    "- **文本字符串docstring**：出现在函数体开头的字符串，对函数的接口进行描述；\n",
    "- **多行字符串multiline string**：用成对的3个连续引号包围的字符串，可以跨越程序的多行；\n",
    "- **前置条件precondition**：调用者在函数运行之前应该满足的条件；\n",
    "- **后置条件postcondition**：在函数结束之前应该满足的条件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bfe2e19",
   "metadata": {},
   "source": [
    "## 练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "9f94061e",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Exception reporting mode: Verbose\n"
     ]
    }
   ],
   "source": [
    "# 这个单元格让Jupyter在出现运行时故障时提供更多调试信息。\n",
    "# 在进行练习前先运行本单元格。\n",
    "\n",
    "%xmode Verbose"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50ed5c38",
   "metadata": {},
   "source": [
    "对于以下的练习，你可能会用到几个函数：\n",
    "\n",
    "- `penup`抬起海龟想象中的笔，从而不在纸上留下踪迹；\n",
    "- `pendown`将笔落下，留下行动的踪迹。\n",
    "\n",
    "下面的函数使用`penup`和`pendown`，移动海龟但不留下踪迹："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "6f9a0106",
   "metadata": {},
   "outputs": [],
   "source": [
    "from jupyturtle import penup, pendown\n",
    "\n",
    "def jump(length):\n",
    "    \"\"\"向前移动`length`的距离，不留下踪迹。\n",
    "    \n",
    "    后置条件：将笔落下。\n",
    "    \"\"\"\n",
    "    penup()\n",
    "    forward(length)\n",
    "    pendown()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c78c1e17",
   "metadata": {},
   "source": [
    "### 练习\n",
    "\n",
    "编写程序`rectangle`，绘制给定宽度width和高度height的矩形。例如绘制一个宽度80,高度40的矩形。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b05078c",
   "metadata": {
    "tags": []
   },
   "source": [
    "你可以使用以下代码测试函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "1311ee08",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "make_turtle()\n",
    "rectangle(80, 40)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b8faaf6",
   "metadata": {},
   "source": [
    "### 练习\n",
    "\n",
    "编写`rhombus`，绘制菱形，接受参数为边长length和内角angle。例如绘制一个边长50,内角60度的菱形。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7917956b",
   "metadata": {
    "tags": []
   },
   "source": [
    "你可以使用以下代码测试函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "1d845de9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "make_turtle()\n",
    "rhombus(50, 60)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9175a90",
   "metadata": {},
   "source": [
    "### 练习\n",
    "\n",
    "编写更泛用的函数`parallelogram`，绘制一个平行四边形。用`parallelogram`重写`rectangle`和`rhombus`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c03bd4a2",
   "metadata": {
    "tags": []
   },
   "source": [
    "你可以使用以下代码测试函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "c8dfebc9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "make_turtle(width=400)\n",
    "jump(-120)\n",
    "\n",
    "rectangle(80, 40)\n",
    "jump(100)\n",
    "rhombus(50, 60)\n",
    "jump(80)\n",
    "parallelogram(80, 50, 60)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "991ab59d",
   "metadata": {},
   "source": [
    "### 练习\n",
    "\n",
    "编写一些合适的泛用函数，绘制像这样的形状：\n",
    "\n",
    "![](jupyturtle_pie.png)\n",
    "\n",
    "提示：编写函数`triangle`绘制单个三角区域，然后编写函数`draw_pie`，使用`triangle`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8702c0ad",
   "metadata": {
    "tags": []
   },
   "source": [
    "你可以使用以下代码测试函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c519ca39",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "turtle = make_turtle(delay=0)\n",
    "jump(-80)\n",
    "\n",
    "size = 40\n",
    "draw_pie(5, size)\n",
    "jump(2*size)\n",
    "draw_pie(6, size)\n",
    "jump(2*size)\n",
    "draw_pie(7, size)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c78b76f",
   "metadata": {},
   "source": [
    "### 练习\n",
    "\n",
    "编写一些合适的泛用函数，绘制像这样的形状：\n",
    "\n",
    "![](jupyturtle_flower.png)\n",
    "\n",
    "提示：使用`arc`编写函数`petal`，绘制一个花瓣。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fe06dea",
   "metadata": {
    "tags": []
   },
   "source": [
    "你可以使用以下代码测试函数。\n",
    "\n",
    "由于需要绘制许多短线段，绘制速度可能很慢。在`make_turtle`中使用`auto_render=False`关键字参数以禁用自动渲染，在最后调用`render`函数以显示绘制结果。\n",
    "\n",
    "当你调试时，你可能想要去除`auto_render=False`参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04193da5",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from jupyturtle import render\n",
    "\n",
    "turtle = make_turtle(auto_render=False)\n",
    "\n",
    "jump(-60)\n",
    "n = 7\n",
    "radius = 60\n",
    "angle = 60\n",
    "flower(n, radius, angle)\n",
    "\n",
    "jump(120)\n",
    "n = 9\n",
    "radius = 40\n",
    "angle = 85\n",
    "flower(n, radius, angle)\n",
    "\n",
    "render()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d9f35d1",
   "metadata": {},
   "source": [
    "### 询问虚拟助手\n",
    "\n",
    "在Python中有一些模块与`jupyturtle`类似，我们本章使用的模块是经过修改的适应本书的版本。\n",
    "\n",
    "如果你询问虚拟助手，它可能不知道用哪个模块。但如果你提供一些示例，它可能会想明白。例如，尝试以下提示词，看看它能否提供绘制螺旋的函数：\n",
    "\n",
    "```txt\n",
    "以下程序使用一个海龟图形模块绘制圆：\n",
    "\n",
    "from jupyturtle import make_turtle, forward, left\n",
    "import math\n",
    "\n",
    "def polygon(n, length):\n",
    "    angle = 360 / n\n",
    "    for i in range(n):\n",
    "        forward(length)\n",
    "        left(angle)\n",
    "        \n",
    "def circle(radius):\n",
    "    circumference = 2 * math.pi * radius\n",
    "    n = 30\n",
    "    length = circumference / n\n",
    "    polygon(n, length)\n",
    "    \n",
    "make_turtle(delay=0)\n",
    "circle(30)\n",
    "\n",
    "编写一个能够绘制螺旋的函数。\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7beb2afe",
   "metadata": {},
   "source": [
    "注意结果中可能有我们还没学习过的功能，也可能包含错误。复制虚拟助手的代码，看看它能否正确工作。如果你没有得到想要的结果。尝试修改提示词。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7f4edf8",
   "metadata": {
    "tags": []
   },
   "source": [
    "[Think Python: 3rd Edition](https://allendowney.github.io/ThinkPython/index.html)\n",
    "\n",
    "Copyright 2024 [Allen B. Downey](https://allendowney.com)\n",
    "\n",
    "Code license: [MIT License](https://mit-license.org/)\n",
    "\n",
    "Text license: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
